home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / scvs / scvs.test < prev    next >
Text File  |  1991-10-31  |  51KB  |  2,201 lines

  1. #! /sprite/cmds/perl 
  2. #
  3. #   Scvs is the "Sprite Concurrent Version System", pronounced "skivies".
  4. #   It is a Perl script wrapper for cvs.  See the cvs man page for more
  5. #   details.
  6. #
  7. # $Header: /sprite/src/cmds/scvs/RCS/scvs,v 1.13 91/10/31 13:08:52 jhh Exp Locker: jhh $ SPRITE (Berkeley)
  8. #
  9. # Copyright 1991 Regents of the University of California
  10. # Permission to use, copy, modify, and distribute this
  11. # software and its documentation for any purpose and without
  12. # fee is hereby granted, provided that this copyright
  13. # notice appears in all copies.  The University of California
  14. # makes no representations about the suitability of this
  15. # software for any purpose.  It is provided "as is" without
  16. # express or implied warranty.
  17. #
  18.  
  19. require "option.pl";
  20. require "pwd.pl";
  21. require "ctime.pl";
  22. require "stat.pl";
  23.  
  24. $recurse = 1;
  25. $verbose = 0;
  26. $linkFile = "links";
  27. $debug = 0;
  28. $configFile = "SCVS.config";
  29. $argFile = "args";
  30. $modNameFile = "moduleName";
  31. $userFile = "SCVS/users";
  32.  
  33. @options = (
  34.     $OPT_NIL, $OPT_DOC, $OPT_NIL, 
  35.     "Usage: scvs [scvs options] command [command options]",
  36.     "V", $OPT_TRUE, *verbose, "Verbose",
  37.     "D", $OPT_TRUE, *debug, "Debug",
  38.     "r", $OPT_FUNC, "CvsOpt1", "Check out files read-only",
  39.     "w", $OPT_FUNC, "CvsOpt1", "Check out files read-write (default)",
  40.     "v", $OPT_FUNC, "CvsOpt1", "Print cvs version info",
  41.     "d", $OPT_STRING, *cvsroot, "Specify cvs root directory",
  42.     "e", $OPT_FUNC, "CvsOpt1", "Specify editor to use",
  43.     "H", $OPT_FUNC, "CvsOpt1", "Print help information",
  44. );
  45. undef($cvsargs);
  46. &Opt_Parse(*ARGV, @options, $OPT_OPTIONS_FIRST);
  47. if ($debug) {
  48.     $verbose = 1;
  49. }
  50. $cvsCmdArgs = $cvsargs;
  51.  
  52. @cvsCmds = ("join", "patch", "tag");
  53.  
  54.  
  55. #
  56. # Config
  57. #
  58. # Find the configuration file and set up various configuration variables.
  59. #
  60. # Results: 0 if successful, 1 otherwise
  61. # Side effects: Some variables are set.
  62. #
  63.  
  64. sub Config {
  65.     local($pwd) = $ENV{'PWD'};
  66.     local($stat, $lastStat) = (0, 0);
  67.     local($tmp);
  68.     local(@attempts);
  69.  
  70.     #
  71.     # Work our way up the directory tree looking for the config file.
  72.     #
  73.     while(! -e $configFile) {
  74.     push(@attempts, $ENV{'PWD'});
  75.     &Chdir("..") == 0 || return 1;
  76.     &Stat(".");
  77.     $stat = $st_dev . $st_ino . $st_serverID;
  78.     last if ($stat eq $lastStat);
  79.     $lastStat = $stat;
  80.     }
  81.     if (! -e $configFile) {
  82.     printf("Couldn't find configuration file\n");
  83.     foreach $tmp (@attempts) {
  84.         printf("Not in $tmp\n");
  85.     }
  86.     return 1;
  87.     }
  88.     open(CONFIG, "$configFile") || die("Can't open $configFile: $!\n");
  89.     while(<CONFIG>) {
  90.     next if (/^\s*#/);
  91.     if (/^cvsroot:\s+(\S+)\s*$/) {
  92.         if (!defined($cvsroot)) {
  93.         $cvsroot = $1;
  94.         }
  95.     } elsif(/^installdir:\s+(\S+)\s*$/) {
  96.         $installdir = $1;
  97.     }
  98.     }
  99.     close(CONFIG);
  100.     if (!defined($cvsroot)) {
  101.     printf("cvsroot not set in config file\n");
  102.     return 1;
  103.     }
  104.     &Chdir("$pwd") == 0 || return 1;
  105.     return 0;
  106. }
  107.  
  108. #
  109. # PackCmd($command, @dirs)
  110. #
  111. # Runs a Pack or Unpack command on each of the directories in the list.
  112. #
  113. # Results: 0 if successful, 1 otherwise
  114. #
  115. # Side effects:  The link file is modified.
  116. #
  117.  
  118. sub PackCmd {
  119.     local($command) = shift;
  120.     local(@dirs) = @_;
  121.     local($status) = 0;
  122.     local($pwd) = $ENV{'PWD'};
  123.  
  124.     if ($#dirs < $[) {
  125.     push(@dirs, '.');
  126.     }
  127.     foreach $dir (@dirs) {
  128.     &Chdir($dir) == 0 || return 1; 
  129.     if ($command eq "pack") {
  130.         $status = &Pack($dir);
  131.     } else {
  132.         $status = &Unpack($dir);
  133.     }
  134.     if ($status) {
  135.         return $status;
  136.     }
  137.     &Chdir($pwd) == 0 || return 1; 
  138.     }
  139. }
  140. #
  141. # Pack($path)
  142. #
  143. # Finds all symbolic links in the current directory and puts them in the
  144. # link file.  The links are stored in alphabetical
  145. # order.  If $recurse is non-zero, Pack will call itself to recurse on
  146. # subdirectories.
  147. #
  148. # Results: 0 if successful, 1 otherwise
  149. #
  150. # Side effects: The link file is modified.
  151. #
  152.  
  153. sub Pack {
  154.     local($path) = shift;
  155.     local($addDir) = 0;
  156.     local($addFile) = 0;
  157.     local(%links);
  158.     local($link);
  159.  
  160.     #
  161.     # Don't pack SCVS subdirectories.
  162.     #
  163.     if ($path =~ m|.*/SCVS|) {
  164.     return 0;
  165.     }
  166.     printf(STDERR "Packing $path\n") if ($debug);
  167.     $addDir = (-d "SCVS") ? 0 : 1;
  168.     $addFile = (-f "SCVS/$linkFile") ? 0 : 1;
  169.     opendir(THISDIR, ".") || return &Error(1, "Opendir of $path failed: $!\n");
  170.     foreach $link (grep(-l, readdir(THISDIR))) {
  171.     printf(STDERR "$link\n") if ($debug);
  172.     $links{$link} = readlink($link);
  173.     }
  174.     close(THISDIR);
  175.     if (defined(%links) || (!$addFile)) {
  176.     if ($addDir) {
  177.         mkdir("SCVS", 0770) ||
  178.         return &Error(1, "Mkdir of SCVS failed: $!\n");
  179.     }
  180.     if (open(PACK, ">SCVS/$linkFile") == 0) {
  181.         printf("Can't open $linkFile: $!\n");
  182.         $status = 1;
  183.         last;
  184.     }
  185.     printf(PACK 
  186.         "# This file is used by scvs and contains symbolic link\n");
  187.     printf(PACK 
  188.         "# information.  Each line is of the form \"link target\"\n");
  189.     printf(PACK "# \$Header\n");
  190.     foreach $link (sort keys %links) {
  191.         printf(PACK "%-24s %s\n", $link, $links{$link});
  192.     }
  193.     close(PACK);
  194.     if ($addFile && (-e "CVS.adm")) {
  195.         if ($addDir) {
  196.         system("cvs -d $cvsroot add SCVS");
  197.         }
  198.         system("cvs -d $cvsroot add -m\"scvs links\" SCVS/$linkFile");
  199.     }
  200.     } 
  201.     if ($recurse) {
  202.     $status = &AllSubdirs($path, "Pack");
  203.     }
  204.     return $status;
  205. }
  206.  
  207. #
  208. # Unpack($path)
  209. #
  210. # Reads the link file in the current directory and creates symbolic links
  211. # from its contents. If recurse is non-zero, Unpack will call itself to 
  212. # recurse on subdirectories.
  213. #
  214. # Results: 0 if successful, 1 otherwise
  215. #
  216. # Side effects: Symbolic links may be created in the current directory
  217. #
  218. sub Unpack {
  219.     local($path) = shift;
  220.     local($status) = 0;
  221.  
  222.     printf(STDERR "Unpacking $path\n") if ($debug);
  223.     if (open(UNPACK, "SCVS/$linkFile")) {
  224.     while(<UNPACK>) {
  225.         next if (/^#/);
  226.         if (/(\S+)\s+(\S+)/) {
  227.         ($link, $value) = ($1, $2);
  228.         if (-l $link) {
  229.             $old = readlink($link);
  230.             if ($old ne $value) {
  231.             printf(
  232.             "Changing $link -> $value, instead of -> $old\n");
  233.             unlink($link);
  234.             } else {
  235.             next;
  236.             }
  237.         } elsif (-e $link) {
  238.             printf("File $link already exists.\n");
  239.             $status = 1;
  240.             next;
  241.         } elsif ($verbose) {
  242.             printf("Creating: $link -> $value\n");
  243.         }
  244.         if (symlink($value, $link) == 0) { 
  245.             printf("Can't create link from $link to $value: $!");
  246.             $status = 1;
  247.         }
  248.         }
  249.     }
  250.     close(UNPACK);
  251.     }
  252.     if ($recurse) {
  253.     $status = &AllSubdirs($path, "Unpack");
  254.     }
  255.     return $status;
  256. }
  257.  
  258. #
  259. # Repository(module)
  260. #
  261. # Finds the pathname of the repository directory for the given module.
  262. #
  263. # Results: The pathname
  264. #
  265. # Side effects: 
  266. #
  267.  
  268. sub Repository {
  269.     local($tmp);
  270.     $tmp = &ReadFile("$_[0]/CVS.adm/Repository", 1);
  271.     if (defined($tmp)) {
  272.     chop($tmp);
  273.     return "$cvsroot/$tmp"; 
  274.     }
  275.     return undef;
  276. }
  277.  
  278. #
  279. # Prune($path)
  280. #
  281. # Removes the given directory if it is empty (no user files or subdirectories).
  282. # Recurses on subdirectories.
  283. #
  284. # Results: 0 if successful, 1 otherwise
  285. #
  286. # Side effects: The directory or its subdirectories may be removed.
  287. #
  288.  
  289. sub Prune {
  290.     local($path) = shift;
  291.     local($i);
  292.     local($status) = 0;
  293.     local($empty) = 1;
  294.     local($tail) = substr($path, rindex($path, '/') + 1);
  295.  
  296.     if ($tail eq "SCVS") {
  297.     return 0;
  298.     }
  299.     print "Pruning $path\n" if ($debug);
  300.     $status = &AllSubdirs($path, "Prune");
  301.     if ($status) {
  302.     return $status;
  303.     }
  304.     opendir(THISDIR, ".") || 
  305.     return &Error(1, "Opendir of $path failed: $!\n"); 
  306.     foreach $i (readdir(THISDIR)) {
  307.     next if ($i eq ".");
  308.     next if ($i eq "..");
  309.     next if ($i eq "CVS.adm");
  310.     next if ($i eq "SCVS");
  311.     print "Found $i in $path\n" if ($debug);
  312.     $empty = 0;
  313.     last;
  314.     }
  315.     close(THISDIR);
  316.     if ($empty) {
  317.     print "Prune: chdir to ..\n" if ($debug);
  318.     &Chdir("..") == 0 || return 1;
  319.     print "Prune: deleting $tail\n" if ($debug);
  320.     system("rm -rf $tail");
  321.     }
  322.     return 0;
  323. }
  324.  
  325. #
  326. # CvsOpt1($optString, $nextArg)
  327. #
  328. # Appends $optString to $cvsargs.
  329. #
  330. # Results: 0 
  331. #
  332. # Side effects: None
  333. #
  334. sub CvsOpt1 {
  335.     printf("CvsOpt1 @_\n") if ($debug);
  336.     $cvsargs .= "$_[0] ";
  337.     return 0;
  338. }
  339.  
  340. #
  341. # CvsOpt2($optString, $nextArg)
  342. #
  343. # Appends $optString and $nextArg to $cvsargs.
  344. #
  345. # Results: 1
  346. #
  347. # Side effects: None
  348. #
  349. sub CvsOpt2 {
  350.     printf("CvsOpt2 @_\n") if ($debug);
  351.     $cvsargs .= "$_[0] \"$_[1]\" ";
  352.     return 1;
  353. }
  354.  
  355.  
  356. #
  357. # Checkout(@modules)
  358. #
  359. # Checks out modules.  "cvs co" is used to make a copy of the module. 
  360. # Unpack is used to unpack symbolic links.  
  361. # The current user name is added to the SCVS.users
  362. # file and a list of any other users with a copy of the module are 
  363. # printed.  Any options passed to "cvs co" are stored in the SCVS/args
  364. # file to be used on subsequent updates.
  365. #
  366. # Results: 0 if successful, 1 otherwise
  367. #
  368. # Side effects: A subdirectory is created for each module.
  369. #
  370.  
  371. sub Checkout {
  372.     local(@modules) = @_;
  373.     local($buffer, $i,$repos, $user, $date, %count, %dates);
  374.     local($found, $name);
  375.     local($prune) = 1;
  376.     local($personal) = 0;
  377.     local($args);
  378.     local(@options) = ( 
  379.     "l", $OPT_FALSE, *recurse, "Don't recurse.",
  380.     "P", $OPT_FALSE, *prune, "Don't prune empty directories.",
  381.     "i", $OPT_TRUE, *personal, "Deviation from standard source tree",
  382.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  383.     "c", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  384.     "Q", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  385.     "q", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  386.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  387.     "n", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  388.     "p", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  389.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  390.     "D", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  391.      );
  392.  
  393.     undef($cvsargs);
  394.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  395.     $args = $cvsargs;
  396.  
  397.     # Put together the "cvs co" command.
  398.  
  399.     $buffer = "cvs -d $cvsroot $cvsCmdArgs co $args";
  400.  
  401.     if ($args =~ /-c/) {
  402.     system("$buffer");
  403.     return 0;
  404.     }
  405.    if (($args =~ /-r/) || ($args =~ /-D/)) {
  406.     $buffer .= "-f ";
  407.     }
  408.     $status = &Lock("r", @modules);
  409.     if ($status) {
  410.     return $status;
  411.     }
  412.     $user = getlogin;
  413.     print "@modules\n" if ($debug);
  414.  
  415. module:
  416.     foreach $i (@modules) {
  417.     local($pwd) = $ENV{'PWD'};
  418.  
  419.     printf("Checking out $i\n") if ($debug);
  420.     # Perform the "cvs co".
  421.  
  422.     printf("$buffer $i \n") if ($debug);
  423.     system("$buffer $i");
  424.  
  425.     # Store the "cvs co" arguments in the info file.
  426.  
  427.     if (! -d "$i/SCVS") {
  428.         if (!mkdir("$i/SCVS", 0770)) {
  429.         $status = &Error(1, "Mkdir of $i/SCVS failed: $!\n");
  430.         next module;
  431.         }
  432.     }
  433.     if (!open(CO, ">$i/SCVS/$argFile")) {
  434.         $status = &Error(1, "Open of $i/SCVS/$argFile failed: $!\n");
  435.         next module;
  436.     }
  437.     print(CO "# This file contains the arguments given when this\n");
  438.     print(CO "# module was checked out.\n");
  439.     print(CO "$cvsCmdArgs\n");
  440.     print(CO "$args\n");
  441.     close(CO);
  442.  
  443.     &Chdir($i) == 0 || return 1; 
  444.  
  445.     # Unpack the module.
  446.     &Unpack($i) == 0 || return &Error("Unpack of $i failed\n");
  447.  
  448.     # Prune any empty directories in the module.
  449.     if ($prune) {
  450.         &Prune($i) == 0 || return &Error(1, "Prune of $i failed\n");
  451.     }
  452.  
  453.     &Chdir($pwd) == 0 || return 1; 
  454.  
  455.     # See if any other users have a copy of the module, and add our
  456.     # own entry.
  457.  
  458.     $repos = &Repository($i);
  459.     next module if (!defined($repos));
  460.     $date = &ctime(time);
  461.     open(CO2, ">$repos/$tmpfile") ||
  462.         return &Error(1, "Open of $repos/$tmpfile failed: $!\n");
  463.     if (-e "$repos/$userFile") {
  464.         local($copy) = 0;
  465.         open(CO1, "$repos/$userFile") ||
  466.         return &Error(1, "Open of $repos/$userFile failed: $!\n");
  467.         while(<CO1>) {
  468.         $copy = 0;
  469.         next if (/^#/);
  470.         if (/^$user\s+([\w\/\.]+)\s+(.*)/) {
  471.             if ($1 eq "$pwd/$i") {
  472.             $copy = 1;
  473.             } else {
  474.             $found = 1;
  475.             push(@mine, $_);
  476.             }
  477.         } elsif (/^(\S+)\s+([\w\/\.]+)\s+(.*)/) {
  478.             $others{$1} = $3;
  479.         }
  480.         }
  481.         continue {
  482.         if (!$copy) {
  483.             print CO2 $_;
  484.         }
  485.         }
  486.         close(CO1);
  487.     } else {
  488.         printf(CO2 "# List of users with copies of this module.\n");
  489.     }
  490.     if ($#mine >= $[) {
  491.         printf("\nYou also have these copies of the $i module:\n");
  492.         print join("\n", @mine);
  493.     }
  494.     printf(CO2 "$user $pwd/$i %s", &ctime(time));
  495.     close(CO2);
  496.     if (!$personal) {
  497.         if (!rename("$repos/$tmpfile", "$repos/$userFile")) {
  498.         printf(
  499.           "Rename of $repos/$tmpfile to $repos/$userFile failed:$!\n");
  500.         unlink("$repos/$tmpfile");
  501.         next module;
  502.         }
  503.     } else {
  504.         unlink("$repos/$tmpfile");
  505.     }
  506.     if (defined(%others)) {
  507.         printf("\nThe following users have copies of the $i module:\n"); 
  508.         while(($name, $date) = each(%others)) {
  509.         printf("$name $date\n");
  510.         }
  511.     }
  512.     }
  513.     return 0;
  514. }
  515.  
  516. #
  517. # UnlockCmd(@ARGV)
  518. #
  519. # Parse arguements, then call Unlock to do the dirty work. 
  520. #
  521. # Results: 0 if successful, 1 otherwise
  522. #
  523. # Side effects: 
  524. #
  525. sub UnlockCmd {
  526.     local(@args) = @_;
  527.     local($all) = 0;
  528.     local($status) = 0;
  529.     local(@options) = (
  530.     "a", $OPT_TRUE, *all, "Remove everybody's locks",
  531.     );
  532.     &Opt_Parse(*args, @options, $OPT_OPTIONS_FIRST);
  533.     $status = &Unlock($all,@args);
  534.     return $status;
  535. }
  536.  
  537.  
  538. #
  539. # Unlock($allusers, @modules)
  540. #
  541. # Remove the locks for a list of modules.  
  542. #
  543. # Results: 0 if successful, 1 otherwise
  544. #
  545. # Side effects: 
  546. #
  547.  
  548. sub Unlock {
  549.     local($allusers) = shift;
  550.     local(@modules) = @_;
  551.     local($cvsdir, $i, $lock);
  552.     local($status) = 0;
  553.     local($user) = getlogin;
  554.  
  555.     print("Unlock $allusers @modules\n") if ($debug);
  556.     if (!defined(%modMap)) {
  557.     &ModMap;
  558.     }
  559.     if ($#modules < $[) {
  560.     push(@modules, ".");
  561.     }
  562. module:
  563.     foreach $i (@modules) {
  564.     if ($i eq ".") {
  565.         $i = &GetModuleName;
  566.         if (!defined($i)) {
  567.         $status = 1;
  568.         next module;
  569.         }
  570.     }
  571.     if (!defined($modMap{$i})) {
  572.         printf(STDERR "Module $i does not exist.\n");
  573.         $status = 1;
  574.         next module;
  575.     }
  576.     $cvsdir = "$cvsroot/$modMap{$i}/SCVS";
  577.     $lock = "$cvsdir/locks";
  578.     if (!-e $lock) {
  579.         next module;
  580.     }
  581.     if ($allusers) {
  582.         if (!unlink($lock)) {
  583.         printf("Can't remove lock file $lock: $!\n");
  584.         }
  585.         next module;
  586.     }
  587.     if (!open(UNLOCK1, "$lock")) {
  588.         print("Open of $lock failed: $!\n");
  589.         next module;
  590.     }
  591.     if (!open(UNLOCK2, ">$cvsdir/$tmpfile")) {
  592.         print("Open of $cvsdir/$tmpfile failed: $!\n");
  593.         next module;
  594.     }
  595.     flock(UNLOCK1, 2) || 
  596.         return &Error(1, "Flock(2) of $lock failed: $!\n");
  597.  
  598.     while(<UNLOCK1>) {
  599.         ($type, $name) = split(' ');
  600.         if ($name ne $user) {
  601.         print(UNLOCK2 $_);
  602.         }
  603.     }
  604.     close(UNLOCK2);
  605.     if (!rename("$cvsdir/$tmpfile", "$lock")) {
  606.         printf(
  607.           "Rename of $cvsdir/$tmpfile to $lock failed:$!\n");
  608.         unlink("$cvsdir/$tmpfile");
  609.         next module;
  610.     }
  611.     }
  612.     return $status;
  613. }
  614.  
  615. #
  616. # LockCmd(@ARGV)
  617. #
  618. # Parse any options then call Lock to do all the work.
  619. #
  620. # Results: 0 if successful, 1 otherwise
  621. #
  622. # Side effects: The lock files in the modules are updated.
  623. #
  624.  
  625. sub LockCmd {
  626.     local(@args) = @_;
  627.     local($write) = 1;
  628.     local($status) = 0;
  629.     local(@options) = (
  630.     "w", $OPT_TRUE, *write, "Write (exclusive) lock",
  631.     "r", $OPT_FALSE, *write, "Read (shared) lock",
  632.     );
  633.     print("LockCmd @args\n") if ($debug);
  634.     &Opt_Parse(*args, @options, $OPT_OPTIONS_FIRST);
  635.     $status = &Lock($write ? "w" : "r", @args);
  636.     undef(@locks);
  637.     return $status;
  638. }
  639.  
  640.  
  641. #
  642. # Lock($type, @modules)
  643. #
  644. # Make sure the modules are unlocked, and lock them.  Any modules that
  645. # we lock are put in the @lock array.  
  646. #
  647. # Results: 0 if successful, 1 otherwise
  648. #
  649. # Side effects: Lock files are created in the modules.
  650. #
  651.  
  652. sub Lock {
  653.     local($type) = shift;
  654.     local(@modules) = @_;
  655.     local($cvsdir);
  656.     local($status) = 0;
  657.     local($i, $name);
  658.     local(@mylocks);
  659.     local($user) = getlogin;
  660.     local(@lockFiles);
  661.     local($prevType);
  662.     local($prevName);
  663.     local($prevDate);
  664.     local(@prevLocks);
  665.     local($lock);
  666.  
  667.     print("Lock $type @modules\n") if ($debug);
  668.     if (!defined(%modMap)) {
  669.     &ModMap;
  670.     }
  671.     if ($#modules < $[) {
  672.     push(@modules, ".");
  673.     }
  674. module:
  675.     foreach $i (@modules) {
  676.     if ($i eq ".") {
  677.         $i = &GetModuleName;
  678.         if (!defined($i)) {
  679.         $status = 1;
  680.         next module;
  681.         }
  682.     }
  683.     if (!defined($modMap{$i})) {
  684.         printf(STDERR "$i module does not exist.\n");
  685.         $status = 1;
  686.         next module;
  687.     }
  688.     $cvsdir = "$cvsroot/$modMap{$i}/SCVS";
  689.     $lock = "$cvsdir/locks";
  690.     print("Cvsdir = $cvsdir\n") if ($debug);
  691.     if (-f "$lock") {
  692.         print("Opening $lock\n") if ($debug);
  693.         open(LOCK1, "$lock") || 
  694.         return &Error(1, "Open of $lock failed: $!\n");
  695.         flock(LOCK1, 2) || 
  696.         return &Error(1, "Flock(2) of $lock failed: $!\n");
  697.         while(<LOCK1>) {
  698.         ($prevType, $prevName) = split(' ');
  699.         if ($prevName eq $user) {
  700.             if ($prevType ne $type) {
  701.             return &Error(1, "$i already locked:\n$_");
  702.             } else {
  703.             close(LOCK1);
  704.             next module;
  705.             }
  706.         } else {
  707.             if (($prevType eq "r") && ($type eq "w")) {
  708.             return &Error(1, "$i already locked:\n$_");
  709.             } elsif ($prevType eq "w") {
  710.             return &Error(1, "$i already locked:\n$_");
  711.             }
  712.         }
  713.         push(@prevLocks, $_);
  714.         }
  715.     }
  716.     open(LOCK2, ">$cvsdir/$tmpfile") ||
  717.         return &Error(1, "Open of $cvsdir/$tmpfile failed: $!\n");
  718.     foreach $i (@prevLocks) {
  719.         print(LOCK2 "$i");
  720.     }
  721.     printf(LOCK2 "$type $user %s", &ctime(time));
  722.     close(LOCK2);
  723.     if (!rename("$cvsdir/$tmpfile", "$lock")) {
  724.         printf(
  725.           "Rename of $cvsdir/$tmpfile to $lock failed:$!\n");
  726.         unlink("$cvsdir/$tmpfile");
  727.         return 1;
  728.     }
  729.     push(@mylocks, $i);
  730.     close(LOCK1);
  731.     }
  732.     if ($status) {
  733.     if (&Unlock(0, @mylocks)) {
  734.         return &Error(1, "Can't clean up in LockCmd\n");
  735.     }
  736.     }
  737.     push(@locks, @mylocks);
  738.     return $status;
  739. }
  740.  
  741. #
  742. # UpdateCmd($lock, @names)
  743. #
  744. # Update modules.  If the arguments are a list of subdirectories then
  745. # we chdir to each of them and run "cvs update".  If the arguments are
  746. # a list of files then we pass them to cvs.  If no files or directories
  747. # are specified then we update the current directory.  The arguments
  748. # for update are retrieved from the SCVS/args file.
  749. #
  750. # Results: 0 if successful, 1 otherwise
  751. #
  752. # Side effects: 
  753. #
  754.  
  755. sub UpdateCmd {
  756.     local($lock) = shift;
  757.     local(@names) = @_;
  758.     local($buffer, $i, $cvsdir, $date, %count, %dates);
  759.     local($found, $name);
  760.     local($module);
  761.     local($pwd);
  762.     local($tmp);
  763.     local($prune);
  764.     local($buildDirs) = 1;
  765.     local($args);
  766.     local(@options) = ( 
  767.     "B", $OPT_FALSE, *buildDirs, "Don't create new directories.",
  768.     "l", $OPT_FALSE, *recurse, "Don't recurse on subdirs",
  769.     "Q", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  770.     "q", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  771.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  772.     "n", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  773.     "p", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  774.     "d", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  775.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  776.     "D", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  777.     );
  778.     undef($cvsargs);
  779.     &Opt_Parse(*names, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  780.     $args = $cvsargs;
  781.  
  782.     # Put together the "cvs update" command.
  783.  
  784.     if ($buildDirs) {
  785.     $args .= "-d ";
  786.     }
  787.     if (! $recurse) {
  788.     $args .= "-l ";
  789.     }
  790.     $buffer = "cvs -d $cvsroot $cvsCmdArgs ";
  791.  
  792.     if ($#names < $[) {
  793.     push(@names, ".");
  794.     }
  795.     if (! -d $names[0]) {
  796.     if ($lock) {
  797.         $status = &Lock("r","."); 
  798.         if ($status) {
  799.         return $status;
  800.         }
  801.     }
  802.     $tmp = "$buffer update $args @names";
  803.     printf("$tmp\n") if ($debug);
  804.     system($tmp);
  805.     $recurse = 0;
  806.     &Unpack(".") == 0 ||
  807.         return &Error(1, "Unpack of current directory failed.\n");
  808.     } else {
  809.     #
  810.     # Lock the modules.
  811.     #
  812.     if ($lock) {
  813.         $status = &Lock("r", @names); 
  814.         if ($status) {
  815.         return $status;
  816.         }
  817.     }
  818.     $pwd = $ENV{'PWD'};
  819. module: 
  820.     foreach $i (@names) {
  821.         $prune = 0;
  822.         &Chdir($i) == 0 || return 1; 
  823.         if (-e "SCVS/$argFile") {
  824.         local(@targs);
  825.         @targs = &ReadFile("SCVS/$argFile", 1);
  826.         if ($targs[1] =~ /(.*)-p(.*)/) {
  827.             $targs[1] = "$1 $2";
  828.             $prune = 1;
  829.         }
  830.         chop($targs[0]);
  831.         chop($targs[1]);
  832.         $tmp = "$buffer $targs[0] update $args $targs[1]";
  833.         printf("$tmp\n") if ($debug);
  834.         }
  835.         system($tmp);
  836.         if (&Unpack($i)) {
  837.         printf(STDERR "Unpack of $i failed.\n");
  838.         $status = 1;
  839.         }
  840.         if ($prune) {
  841.         if (&Prune($i)) {
  842.             printf(STDERR "Prune of $i failed.\n");
  843.             $status = 1;
  844.         }
  845.         }
  846.  
  847.         &Chdir($pwd) == 0 || return 1; 
  848.     }
  849.     }
  850.     return $status;
  851. }
  852.  
  853. #
  854. # Changed($path)
  855. #
  856. # Use the "cvs info" command to see if the contents of the current directory
  857. # or its subdirectories have been changed by the user.  The modified
  858. # parameter is set to 1 if they have been.
  859. #
  860. # Results: 0 if successful, 1 otherwise; 0 if not modified, 1 otherwise
  861. #
  862. # Side effects: 
  863. #
  864. sub Changed {
  865.     local($path) = shift;
  866.     local($modified) = 0;
  867.     local($status) = 0;
  868.     if (!-d "CVS.adm") {
  869.     return 0;
  870.     }
  871.     open(CHG, "cvs -d $cvsroot info |") ||
  872.     return &Error(1, "Can't do cvs info on $path: $!\n");
  873.     while (<CHG>) {
  874.     if (/^[MC]\s+(\S+)/) {
  875.         printf("$path/$1 has been modified\n");
  876.         $modified = 1;
  877.     } elsif(/^A\s+(\S+)/) {
  878.         printf("$path/$1 has been added\n");
  879.         $modified = 1;
  880.     } elsif(/^R\s+(\S+)/) {
  881.         printf("$path/$1 has been deleted\n");
  882.         $modified = 1;
  883.     }
  884.     }
  885.     close(CHG);
  886.     ($status, @results) = &AllSubdirs($path, "Changed");
  887.     if ($status) {
  888.     return $status;
  889.     }
  890.     while ($#results >= $[) {
  891.     local($substatus) = shift(@results);
  892.     local($submod) = shift(@results);
  893.     if ($substatus) {
  894.         $status = 1;
  895.     }
  896.     if ($submod) {
  897.         $modified = 1;
  898.     }
  899.     }
  900.     return ($status, $modified);
  901. }
  902.  
  903. #
  904. # DoneCmd(@modules)
  905. #
  906. # Process the "done" command.  The user is deleted from the list of users
  907. # for each module.  If the -d flag is specified then the snapshot is
  908. # deleted as well.  If the user has made changes to the snapshot the user
  909. # is warned before the "done" command is completed.
  910. #
  911. # Results: 0 if successful, 1 otherwise
  912. #
  913. # Side effects: 
  914. #
  915. sub DoneCmd {
  916.     local(@modules) = @_;
  917.     local($status) = 0;
  918.     local($i);
  919.     local($me) = getlogin;
  920.     local($pwd) = $ENV{'PWD'};
  921.     local($repos, $found);
  922.     local($delete);
  923.     local($modified);
  924.     local(@options) = (
  925.     "d", $OPT_TRUE, *delete, "Delete module",
  926.     );
  927.  
  928.     $recurse = 1;
  929.     undef($cvsargs);
  930.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST); 
  931.     if ($#modules < $[) {
  932.     return &Error(1, "Done command requires a list of modules\n");
  933.     }
  934.     # Make sure all the modules are unlocked, then lock them.
  935.     $status = &Lock("r",@modules); 
  936.     if ($status) {
  937.     return $status;
  938.     }
  939. module:
  940.     foreach $i (@modules) {
  941.     $ok = 0;
  942.     if (! -d $i) {
  943.         printf("Directory $i not found.\n");
  944.         next module;
  945.     }
  946.     &Chdir($i) == 0 || return 1; 
  947.     ($status, $modified) = &Changed($i);
  948.     if ($status) {
  949.         printf(STDERR "Unable to determine if $i module has changed.\n");
  950.         $modified = 1;
  951.     }
  952.     if ($modified == 1) {
  953.         printf("Do you wish to continue? [y/n] ");
  954. prompt:
  955.         while(1) {
  956.         $answer = <STDIN>;
  957.         chop($answer);
  958.         last prompt if ($answer eq "y");
  959.         next module if ($answer eq "n");
  960.         printf("Please answer with \"y\" or \"n\": ");
  961.         }
  962.     } elsif ($modified == 1) {
  963.         next module;
  964.     }
  965.  
  966.     # Update the user file.
  967.     $repos = &Repository(".");
  968.     next module if (!defined($repos));
  969.     if (!open(DONE1, "$repos/$userFile")) {
  970.         printf("Module $i is not checked out\n");
  971.         next module;
  972.     }
  973.     if (!open(DONE2, ">$repos/$tmpfile")) {
  974.         printf("Can't open $repos/$tmpfile: $!\n");
  975.         $status = 1;
  976.         next module;
  977.     }
  978.     $me = getlogin;
  979.     $found = 0;
  980.     while (<DONE1>) {
  981.         if (/^$me\s+([\w\/\.]+)\s+(.*)/) {
  982.         if ($1 eq "$pwd/$i") {
  983.             $found = 1;
  984.             next;
  985.         }
  986.         }
  987.         print DONE2 $_;
  988.     }
  989.     close(DONE1);
  990.     close(DONE2);
  991.     if (!$found) {
  992.         printf("Module $i is not checked out\n");
  993.         next module;
  994.     }
  995.     if (!rename("$repos/$tmpfile", "$repos/$userFile")) {
  996.         printf("Rename of $repos/$tmpfile to $repos/$userFile failed:$!\n");
  997.         unlink("$repos/$tmpfile");
  998.         next module;
  999.     }
  1000.     $ok = 1;
  1001.     }
  1002.     continue {
  1003.     &Chdir($pwd) == 0 || return 1; 
  1004.     if ($ok && $delete) {
  1005.         system("rm -rf $i");
  1006.         if ($?) {
  1007.         printf("Delete of $i failed: $?\n");
  1008.         }
  1009.     }
  1010.     }
  1011.     return $status;
  1012. }
  1013.  
  1014. #
  1015. # AllSubdirs(path, routine, args)
  1016. #
  1017. # Call a routine for each subdirectory of the current directory. The
  1018. # current working directory is changed to the subdirectory before the 
  1019. # routine is called, and the path is modified to reflect this change.
  1020. # The path is passed to the routine when it is called. The routine is
  1021. # called for all subdirectories even if one returns an non-zero status,
  1022. # although this function will then return a non-zero status.
  1023. # Any additional arguments for the routine are passed after the path
  1024. # argument.
  1025. #
  1026. # Results: 0 if successful, 1 if the routine returned non-zero for any
  1027. #         of the subdirectories.
  1028. #
  1029. # Side effects: 
  1030. #
  1031. sub AllSubdirs {
  1032.     local($path) = shift;
  1033.     local($routine) = shift;
  1034.     local($pwd) = $ENV{'PWD'};
  1035.     local($substatus);
  1036.     local($dir);
  1037.     local(@results);
  1038.     local(@status);
  1039.     local(@subdirs);
  1040.  
  1041.     printf(STDERR "AllSubdirs of $routine on $pwd\n") if ($debug);
  1042.     opendir(THISDIR, ".") || 
  1043.     return &Error(1, "Opendir of $path failed: $!\n"); 
  1044.     @subdirs = grep((-d) && (!/^\./) && (! -l) && ($_ ne 'CVS.adm'), 
  1045.             readdir(THISDIR));
  1046.     print("AllSubdirs: @subdirs\n") if ($debug);
  1047.     close(THISDIR);
  1048.     print "@subdirs\n****\n" if ($debug); 
  1049.     foreach $dir (@subdirs) {
  1050.     printf("\t$dir\n") if ($debug);
  1051.     &Chdir($dir) == 0 || return 1; 
  1052.     push(@results, &$routine($path . "/$dir", @_));
  1053.     &Chdir($pwd) == 0 || ($status = 1); 
  1054.     }
  1055.     if (wantarray) {
  1056.     return ($status, @results);
  1057.     }
  1058.     if ($status) {
  1059.     return $status;
  1060.     }
  1061.     @status = grep("$_ != 0", @results);
  1062.     if ($#status >= $[) {
  1063.     return $status[0];
  1064.     }
  1065.     return 0;
  1066. }
  1067.  
  1068.  
  1069. #
  1070. # VerifyCurrent($path, *stale, *modified)
  1071. #
  1072. # Check the status of the files in the current directory and its 
  1073. # subdirectories to see if they are out of date.
  1074. #
  1075. # Results: 0 if successful, 1 otherwise;
  1076. #
  1077. # Side effects: 
  1078. #
  1079. sub VerifyCurrent {
  1080.     local($path) = shift;
  1081.     local(*stale) = shift;
  1082.     local(*modified) = shift;
  1083.     local($pwd) = $ENV{'PWD'};
  1084.     local($status) = 0;
  1085.     local($substatus) = 0;
  1086.     local($current) = 1;
  1087.     local($mod) = 0;
  1088.  
  1089.     printf("Verifying that $path is current\n") if ($debug);
  1090.     if (!-d "CVS.adm") {
  1091.     return 0;
  1092.     }
  1093.     open(CHK, "cvs -d $cvsroot info |") ||
  1094.     return &Error(1, "Can't get info for $path: $!\n");
  1095.     while(<CHK>) {
  1096.     if (/^U\s+(\S+)/) {
  1097.         printf("File $path/$1 is out of date or needs to be added.\n");
  1098.         $current = 0;
  1099.     } elsif (/^D\s+(\S+)/) {
  1100.         printf("File $path/$1 has been removed from the repository.\n");
  1101.         $current = 0;
  1102.     } elsif (/^C\s+(\S+)/) {
  1103.         printf("File $path/$1 is out of date.\n");
  1104.         $current = 0;
  1105.     } elsif (/^[MARC]/) {
  1106.         $mod = 1;
  1107.     } 
  1108.     }
  1109.     close(CHK);
  1110.     if (!$current) {
  1111.     printf("$path is not current\n") if ($debug);
  1112.     push(@stale, $path);
  1113.     }
  1114.     if ($mod) {
  1115.     printf("$path has been modified\n") if ($debug);
  1116.     push(@modified, $path);
  1117.     }
  1118.     if ($recurse) {
  1119.     $status = &AllSubdirs($path, "VerifyCurrent", *stale, *modified);
  1120.     }
  1121.     return $status;
  1122. }
  1123.  
  1124. #
  1125. # UpdateInstalled(@files)
  1126. #
  1127. # Update the installed copy of the sources.  This is done on commit.
  1128. # If @files is not specified then the entire directory and its subdirectories
  1129. # are updated.
  1130. #
  1131. # Results: 0 if successful, 1 otherwise
  1132. #
  1133. # Side effects: The installed sources are updated.
  1134. #
  1135. sub UpdateInstalled {
  1136.     local(@files) = @_;
  1137.     local($dir);
  1138.     local($pwd) = $ENV{'PWD'};
  1139.     local($saveArgs) = $cvsCmdArgs;
  1140.  
  1141.     printf(STDERR "UpdateInstalled\n") if ($debug);
  1142.     $cvsCmdArgs = "-r";
  1143.     $dir = &ReadFile("CVS.adm/Repository", 1);
  1144.     if (!defined($dir)) {
  1145.     return 1;
  1146.     }
  1147.     chop($dir);
  1148.     &Chdir("$installdir/$dir") == 0 || return 1;
  1149.     &UpdateCmd(0, "-Q", @files) == 0 || return 1;
  1150.     &Chdir("$pwd") == 0 || return 1;
  1151.     $cvsCmdArgs = $saveArgs;
  1152.     return 0;
  1153. }
  1154.  
  1155.  
  1156.  
  1157. #
  1158. # Commit
  1159. #
  1160. # Commit the current directory and its subdirectories.
  1161. #
  1162. # Results: 0 if successful, 1 otherwise
  1163. #
  1164. # Side effects: 
  1165. #
  1166. sub Commit {
  1167.     local($path) = shift;
  1168.     local($args) = shift;
  1169.     local($pwd) = $ENV{'PWD'};
  1170.     local($status) = 0;
  1171.     local($output);
  1172.     local($tail);
  1173.  
  1174.  
  1175.     printf(STDERR "CommitDir $path\n") if ($debug);
  1176.     if (!-d "CVS.adm") {
  1177.     return 0;
  1178.     }
  1179.     printf("$path:\n");
  1180.     $tail = substr($path, rindex($path, '/') + 1);
  1181.     #
  1182.     # Before we commit the SCVS links file we remove all the deleted links
  1183.     # from it.
  1184.     #
  1185.     if ($tail eq "SCVS") {
  1186.     if (open(CMTDIR1, "$linkFile")) {
  1187.         open(CMTDIR2, ">$tmpfile") ||
  1188.         return &Error(1, "Open of $path/$tmpfile failed: $!\n");
  1189.         while(<CMTDIR1>) {
  1190.         next if (/^[*]/);
  1191.         print CMTDIR2 $_;
  1192.         }
  1193.         close(CMTDIR1);
  1194.         close(CMTDIR2);
  1195.         if (!rename("$tmpfile", "$linkFile")) {
  1196.         printf("Rename of $tmpfile to $linkFile failed:$!\n");
  1197.         unlink("$tmpfile");
  1198.         return 1;
  1199.         }
  1200.         system("cvs -d $cvsroot $cvsCmdArgs ci -f -m scvs links");
  1201.     }
  1202.     }
  1203.     system("cvs -d $cvsroot $cvsCmdArgs ci -f -a $args");
  1204.     return $status;
  1205. }
  1206.  
  1207. #
  1208. # CommitCmd(@names)
  1209. #
  1210. # Commit any changes to the modules or files. 
  1211. # Otherwise all changed files in the current directory and any subdirectories
  1212. # are committed.  Before anything is committed it is checked that all
  1213. # files are up-to-date.  If they aren't, a message is printed and the
  1214. # commit is not done.
  1215. #
  1216. # Results: 0 if successful, 1 otherwise
  1217. #
  1218. # Side effects: 
  1219. #
  1220.  
  1221. sub CommitCmd {
  1222.     local(@names) = @_;
  1223.     local($pwd, $i);
  1224.     local($status) = 0;
  1225.     local($path);
  1226.     local(@stale, @modified);
  1227.     local($tmp);
  1228.     local($args);
  1229.     local(@options) = (
  1230.     "l", $OPT_FALSE, *recurse, "Don't recurse on subdirs",
  1231.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1232.     "n", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1233.     "m", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1234.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1235.     );
  1236.  
  1237.     $recurse = 1;
  1238.     undef($cvsargs);
  1239.     &Opt_Parse(*names, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  1240.     $args = $cvsargs;
  1241.  
  1242.     if ($#names < $[) {
  1243.     push(@names, ".");
  1244.     }
  1245.     $args .= " -q";
  1246.     if (! -d $names[0]) {
  1247.     $status = &Lock("w","."); 
  1248.     if ($status) {
  1249.         return $status;
  1250.     }
  1251.     $status = &VerifyCurrent(".", *stale, *modified);
  1252.     if ($status) {
  1253.         return $status;
  1254.     }
  1255.     if ($#stale >= $[) {
  1256.         printf("Update your sources using \"scvs update\".\n");
  1257.         return $status;
  1258.     }
  1259.     $tmp = "cvs -d $cvsroot $cvsCmdArgs ci -f $args @names";
  1260.     system($tmp);
  1261.     $status = &UpdateInstalled(@names);
  1262.     } else {
  1263.     $status = &Lock("w",@names); 
  1264.     if ($status) {
  1265.         return $status;
  1266.     }
  1267.     $pwd = $ENV{'PWD'};
  1268.  
  1269.     #
  1270.     # All the modules and their subdirectories must be up-to-date.
  1271.     #
  1272. module:
  1273.     foreach $i (@names) {
  1274.         &Chdir($i) == 0 || return 1; 
  1275.         $status = &VerifyCurrent($i, *stale, *modified);
  1276.         if ($status) {
  1277.         return $status;
  1278.         }
  1279.         &Chdir($pwd) == 0 || return 1; 
  1280.     }
  1281.     
  1282.     if ($#stale >= $[) {
  1283.         printf("Update your sources using \"scvs update\".\n");
  1284.         return $status;
  1285.     }
  1286.     
  1287.     #
  1288.     # Commit all directories that were modified.
  1289.     #
  1290.     foreach $i (@modified) {
  1291.         &Chdir($i) == 0 || return 1; 
  1292.         $status = &Commit($i, $args);
  1293.         last if ($status);
  1294.         if (defined($installdir)) {
  1295.         $status = &UpdateInstalled;
  1296.         last if ($status);
  1297.         }
  1298.         &Chdir($pwd) == 0 || return 1; 
  1299.     }
  1300.     }
  1301.     return $status;
  1302. }
  1303.  
  1304.  
  1305. #
  1306. # WhoCmd(@modules)
  1307. #
  1308. # Print the names of users who have the modules checked out.
  1309. #
  1310. # Results: 0 if successful, 1 otherwise
  1311. #
  1312. # Side effects: 
  1313. #
  1314.  
  1315. sub WhoCmd {
  1316.     local(@modules) = @_;
  1317.     local($pwd, $i);
  1318.     local($status) = 0;
  1319.     local($cvsdir, @who, $user, %users, $line);
  1320.  
  1321.     if (!defined(%modMap)) {
  1322.     &ModMap;
  1323.     }
  1324.     if ($#modules < $[) {
  1325.     push(@modules, ".");
  1326.     }
  1327.     $status = &Lock("r",@modules); 
  1328.     if ($status) {
  1329.     return $status;
  1330.     }
  1331.     $pwd = $ENV{'PWD'};
  1332.  
  1333. module:
  1334.     foreach $i (@modules) {
  1335.     if ($i eq ".") {
  1336.         $i = &GetModuleName;
  1337.         if (!defined($i)) {
  1338.         $status = 1;
  1339.         next module;
  1340.         }
  1341.     }
  1342.     if (!defined($modMap{$i})) {
  1343.         printf(STDERR "$i module does not exist.\n");
  1344.         $status = 1;
  1345.         next module;
  1346.     }
  1347.     $cvsdir = $cvsroot . "/" . $modMap{$i};
  1348.     @who = &ReadFile("$cvsdir/$userFile", 1);
  1349.     foreach $line (@who) {
  1350.         ($user) = split(' ', $line);
  1351.         $users{$user} = 1;
  1352.     }
  1353.     foreach $user (keys %users) {
  1354.         printf("$user\n");
  1355.     }
  1356.     }
  1357.     return $status;
  1358. }
  1359.  
  1360. #
  1361. # AddCmd(@names)
  1362. #
  1363. # Add a file, directory, or symbolic link to a directory.
  1364. #
  1365. # Results: 0 if successful, 1 otherwise
  1366. #
  1367. # Side effects: 
  1368. #
  1369.  
  1370. sub AddCmd {
  1371.     local(@names) = @_;
  1372.     local($i);
  1373.     local($status) = 0;
  1374.     local(%links);
  1375.     local($pwd) = $ENV{'PWD'};
  1376.     local($module);
  1377.     local($args);
  1378.     local(@options) = (
  1379.     "m", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1380.     );
  1381.  
  1382.     undef($cvsargs);
  1383.     &Opt_Parse(*names, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  1384.     $args = $cvsargs;
  1385.  
  1386.     if ($#names < $[) {
  1387.     return &Error(1, "Add command requires list of files\n");
  1388.     }
  1389.     $module = &GetModuleName;
  1390.     if (!defined($module)) {
  1391.     return 1;
  1392.     }
  1393. name:
  1394.     foreach $i (@names) {
  1395.     if (-l $i) {
  1396.         local($target) = readlink($i);
  1397.         if (!defined($target)) {
  1398.         printf("$i does not exist\n");
  1399.         $status = 1;
  1400.         next name;
  1401.         }
  1402.         if (open(ADD, "SCVS/$linkFile")) {
  1403.         while(<ADD>) {
  1404.             if (/^$i\s+(\S+)/) {
  1405.             if ($target ne $1) {
  1406.                 printf("Link $i already points to $1.\n");
  1407.             } else {
  1408.                 printf("Link $i already added.\n");
  1409.             }
  1410.             $status = 1;
  1411.             close(ADD);
  1412.             next name;
  1413.             }
  1414.         }
  1415.         close(ADD);
  1416.         } elsif (! -f "SCVS/$linkFile") {
  1417.         open(ADD, ">SCVS/$linkFile") ||
  1418.             return &Error(1, "Can't open SCVS/$linkFile: $!\n");
  1419.         printf(ADD 
  1420.         "# This file is used by scvs and contains symbolic link\n");
  1421.         printf(ADD 
  1422.         "# information.  Each line is of the form \"link target\"\n");
  1423.         printf(ADD "# \$Header\n");
  1424.         close(ADD);
  1425.         &Chdir("SCVS") == 0 || return 1; 
  1426.         printf("Adding $linkFile directory\n") if ($debug);
  1427.         system("cvs -d $cvsroot add -m \"sym links\" $linkFile");
  1428.         &Chdir($pwd) == 0 || return 1; 
  1429.         } else {
  1430.         return &Error(1, "Open of SCVS/$linkFile failed: $!\n");
  1431.         }
  1432.         $links{$i} = $target;
  1433.     } else {
  1434.         system("cvs -d $cvsroot $cvsCmdArgs add $args $i");
  1435.         if (-d $i) {
  1436.         # 
  1437.         # If we are adding a directory then we should create an
  1438.         # SCVS subdirectory in it.
  1439.         #
  1440.         if (! -d "$i/SCVS") {
  1441.             mkdir("$i/SCVS", 0770) ||
  1442.             return &Error(1, "Mkdir of $i/SCVS failed: $!\n");
  1443.             &Chdir("$i/SCVS") == 0 || return 1; 
  1444.             open(ADD, ">module") ||
  1445.             return &Error(1, "Open of $i/SCVS/module failed: $!\n");
  1446.             printf(ADD "$module\n");
  1447.             close(ADD);
  1448.             system("cvs -d $cvsroot add module");
  1449.             &Chdir($pwd) == 0 || return 1; 
  1450.         }
  1451.         }
  1452.     }
  1453.     if (defined(%links)) {
  1454.         open(ADD, ">>SCVS/$linkFile") ||
  1455.         return &Error(1, "Open of SCVS/$linkFile failed: $!\n");
  1456.         while (($i, $target) = each(%links)) {
  1457.         printf("Adding link $i -> $target\n") if ($debug);
  1458.         printf(ADD "%-24s %s\n", $i, $target);
  1459.         }
  1460.         close(ADD);
  1461.     }
  1462.     }
  1463.     return $status;
  1464. }
  1465. #
  1466. # RemoveCmd(@names)
  1467. #
  1468. # Removes a file, directory, or symbolic link from a directory.
  1469. #
  1470. # Results: 0 if successful, 1 otherwise
  1471. #
  1472. # Side effects: 
  1473. #
  1474.  
  1475. sub RemoveCmd {
  1476.     local(@names) = @_;
  1477.     local($i);
  1478.     local($status, %links, @delete) = 0;
  1479.  
  1480.     if ($#names < $[) {
  1481.     return &Error(1, "Remove command requires list of files\n");
  1482.     }
  1483.     if (open(RM, "SCVS/$linkFile")) {
  1484.     while(<RM>) {
  1485.         next if (/^#/);
  1486.         if (/^([^*]\S+)\s+(\S+)/) {
  1487.         printf("Found link $1 -> $2\n") if ($debug);
  1488.         $links{$1} = $2;
  1489.         }
  1490.     }
  1491.     close(RM);
  1492.     }
  1493. name:
  1494.     foreach $i (@names) {
  1495.     if (-e $i) {
  1496.         printf("$i still exists, deleting it\n");
  1497.         if (!unlink("$i")) {
  1498.         printf("Delete failed: $!\n");
  1499.         $status = 1;
  1500.         next name;
  1501.         }
  1502.     }
  1503.     if (defined($links{$i})) {
  1504.         printf("Putting $i on delete list\n") if ($debug);
  1505.         push(@delete, $i);
  1506.     } else {
  1507.         system("cvs -d $cvsroot $cvsCmdArgs remove $i");
  1508.     }
  1509.     }
  1510.     if ($#delete >= $[) {
  1511.     if (!open(RM1, "SCVS/$linkFile")) {
  1512.         printf("Can't open SCVS/$linkFile: $!\n");
  1513.         $status = 1;
  1514.         next name;
  1515.     }
  1516.     if (!open(RM2, ">$tmpfile")) {
  1517.         printf("Can't open $tmpfile: $!\n");
  1518.         $status = 1;
  1519.         next name;
  1520.     }
  1521. line:
  1522.     while (<RM1>) {
  1523.         if (/^([^#*]\S+)\s+(\S+)/) {
  1524.         for ($i = 0; $i <= $#delete; $i++) {
  1525.             if ($delete[$i] eq $1) {
  1526.             splice(@delete, $i, 1);
  1527.             print RM2 "*$_";
  1528.             next line;
  1529.             }
  1530.         }
  1531.         }
  1532.         print RM2 $_;
  1533.     }
  1534.     close(RM1);
  1535.     close(RM2);
  1536.     if (!rename("$tmpfile", "SCVS/$linkFile")) {
  1537.         printf("Rename of $tmpfile to SCVS/$linkFile failed:$!\n");
  1538.         unlink("$tmpfile");
  1539.         $status = 1;
  1540.     }
  1541.     }
  1542.     return $status;
  1543. }
  1544. #
  1545. # Info($path)
  1546. #
  1547. # Prints out status information for the current directory and recurses
  1548. # on subdirectories.
  1549. #
  1550. # Results: 0 if successful, 1 otherwise
  1551. #
  1552. # Side effects: 
  1553. #
  1554. sub Info {
  1555.     local($path) = shift;
  1556.     local($tail);
  1557.     local($diff) = 0;
  1558.     local($cat) = 0;
  1559.     local($i);
  1560.     local($pwd) = $ENV{'PWD'};
  1561.  
  1562.     if (!-d "CVS.adm") {
  1563.     return 0;
  1564.     }
  1565.     $tail = substr($path, rindex($path, '/') + 1);
  1566.     if ($tail eq "SCVS") {
  1567.     return 0;
  1568.     }
  1569.     system("cvs -d $cvsroot $cvsCmdArgs info ");
  1570.     if (-d "SCVS") {
  1571.     &Chdir("SCVS") == 0 || return 1;
  1572.     open(INFO, "cvs -d $cvsroot $cvsCmdArgs info |") ||
  1573.         return &Error(1, "Can't do cvs info on $path: $!\n");
  1574.     while(<INFO>) {
  1575.         if (/^[UMC]\s+$linkFile/) {
  1576.         $diff = 1;
  1577.         last;
  1578.         } elsif (/^[AD]\s+$linkFile/) {
  1579.         $cat = 1;
  1580.         last;
  1581.         }
  1582.     }
  1583.     close(INFO);
  1584.     if ($diff) {
  1585.         local(%updated);
  1586.         open(INFO, "cvs -d $cvsroot diff $linkFile |") ||
  1587.         return &Error(1, "Can't do cvs diff on $path/$linkFile: $!\n");
  1588.         while(<INFO>) {
  1589.         if (/^>\s+([^*]\S+)/) {
  1590.             printf("A %s\@\n", $1);
  1591.         } elsif (/^>\s+[*](\S+)/) {
  1592.             printf("R %s\@\n", $1);
  1593.             delete $updated{$1};
  1594.         } elsif (/^<\s+([^*]\S+)/) {
  1595.             $updated{$1} = 1;
  1596.         } elsif (/^<\s+[*](\S+)/) {
  1597.             printf("D %s\@\n", $1);
  1598.         }
  1599.         }
  1600.         close(INFO);
  1601.         foreach $i (keys %updated) {
  1602.         printf("U %s\@\n", $i);
  1603.         }
  1604.     }
  1605.     if ($cat) {
  1606.         open(INFO, "$linkFile") ||
  1607.         return &Error(1, "Open of $linkFile failed: $!\n");
  1608.         while(<INFO>) {
  1609.         next if (/^#/);
  1610.         if (/^([^*]\S+)/) {
  1611.             printf("A %s\@\n", $1);
  1612.         } elsif (/^([*]\S+)/) {
  1613.             printf("R %s\@\n", $1);
  1614.         }
  1615.         }
  1616.         close(INFO);
  1617.     }
  1618.     &Chdir($pwd) == 0 || return 1;
  1619.     }
  1620.     if (($recurse) && ($#files < $[)) {
  1621.     $status = &AllSubdirs($path, "Info");
  1622.     }
  1623. }
  1624.  
  1625. #
  1626. # InfoCmd(@modules)
  1627. #
  1628. # Prints out status information for the given modules.
  1629. #
  1630. # Results: 0 if successful, 1 otherwise
  1631. #
  1632. # Side effects: 
  1633. #
  1634.  
  1635. sub InfoCmd {
  1636.     local(@modules) = @_;
  1637.     local($pwd, $i);
  1638.     local($status) = 0;
  1639.     local(@options) = ("l", $OPT_FALSE, *recurse, "Don't recurse on subdirs");
  1640.     local(@targs);
  1641.  
  1642.     $recurse = 1;
  1643.     undef($cvsargs);
  1644.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST);
  1645.     print "@modules\n" if ($debug);
  1646.  
  1647.     if ($#modules < $[) {
  1648.     push(@modules, ".");
  1649.     }
  1650.     if (-e "SCVS/$argFile") {
  1651.     @targs = &ReadFile("SCVS/$argFile", 1);
  1652.     if ($targs[1] =~ /(.*)-p(.*)/) {
  1653.         $targs[1] = "$1 $2";
  1654.     }
  1655.     chop($targs[0]);
  1656.     $cvsCmdArgs .= $targs[0];
  1657.     }
  1658.     if (! -d $modules[0]) {
  1659.     $status = &Lock("r","."); 
  1660.     if ($status) {
  1661.         return $status;
  1662.     }
  1663.     system("cvs -d $cvsroot $cvsCmdArgs info @modules");
  1664.     } else {
  1665.     $status = &Lock("r",@modules);
  1666.     if ($status) {
  1667.         return $status;
  1668.     }
  1669.     $pwd = $ENV{'PWD'};
  1670.     foreach $i (@modules) {
  1671.         printf("InfoCmd %i\n") if ($debug);
  1672.         &Chdir($i) == 0 || return 1; 
  1673.         $status = &Info($i);
  1674.         if ($status) {
  1675.         return $status;
  1676.         }
  1677.         &Chdir($pwd) == 0 || return 1; 
  1678.     }
  1679.     }
  1680.     return $status;
  1681. }
  1682.  
  1683. #
  1684. # DiffFile($path, $file, $args, $current)
  1685. #
  1686. # Prints out status information for the current directory and recurses
  1687. # on subdirectories.
  1688. #
  1689. # Results: 0 if successful, 1 otherwise
  1690. #
  1691. # Side effects: 
  1692. #
  1693. sub DiffFile {
  1694.     local($path) = shift;    # Current path.
  1695.     local($file) = shift;    # File to diff.
  1696.     local($args) = shift;    # args to cvs diff.
  1697.     local($current) = shift;    # Should we diff with current version.
  1698.     local($tail);
  1699.     local($pwd) = $ENV{'PWD'};
  1700.     local($status) = 0;
  1701.     local($version) = "";
  1702.     local($repository);
  1703.  
  1704.     if (!-d "CVS.adm") {
  1705.     return 0;
  1706.     }
  1707.     $repository = &Repository(".");
  1708.     if (!defined($repository)) {
  1709.     print("Repository not found\n") if ($debug);
  1710.     return 0;
  1711.     }
  1712.     printf("Repository is $repository\n") if ($debug);
  1713.     if (!-e "$repository/$file,v") {
  1714.     return 0;
  1715.     }
  1716.     if ($current) {
  1717.     open(DIFF, "cvs -d $cvsroot status $file |") ||
  1718.         return &Error(1, "Can't get status for $path/$file: $!\n");
  1719.     while(<DIFF>) {
  1720.         if (/^RCS:\s+(\S+)/) {
  1721.         $version = "-r $1";
  1722.         last;
  1723.         }
  1724.     }
  1725.     close(DIFF);
  1726.     }
  1727.     system("cvs -d $cvsroot $cvsCmdArgs diff $version $args $file");
  1728. }
  1729.  
  1730. #
  1731. # Diff($path, $args, $current)
  1732. #
  1733. # Prints out status information for the current directory and recurses
  1734. # on subdirectories.
  1735. #
  1736. # Results: 0 if successful, 1 otherwise
  1737. #
  1738. # Side effects: 
  1739. #
  1740. sub Diff {
  1741.     local($path) = shift;    # Current path.
  1742.     local($args) = shift;    # args to cvs diff.
  1743.     local($current) = shift;    # Should we diff with current version.
  1744.     local($tail);
  1745.     local($pwd) = $ENV{'PWD'};
  1746.     local($file);
  1747.     local($status) = 0;
  1748.  
  1749.     if (!-d "CVS.adm") {
  1750.     return 0;
  1751.     }
  1752.     $tail = substr($path, rindex($path, '/') + 1);
  1753.     if ($tail eq "SCVS") {
  1754.     return 0;
  1755.     }
  1756.     opendir(THISDIR, ".") || return &Error(1, "Opendir of $path failed: $!\n");
  1757.     foreach $file (grep(-f, readdir(THISDIR))) {
  1758.     printf(STDERR "$file\n") if ($debug);
  1759.     $status = &DiffFile($path, $file, $args, $current);
  1760.     if ($status) {
  1761.         return $status;
  1762.     }
  1763.     }
  1764.     if ($recurse) {
  1765.     $status = &AllSubdirs($path, "Diff", $args, $current);
  1766.     }
  1767. }
  1768.  
  1769.  
  1770.  
  1771. #
  1772. # DiffCmd(@modules)
  1773. #
  1774. # Does an rcsdiff on the modules or directories
  1775. #
  1776. # Results: 0 if successful, 1 otherwise
  1777. #
  1778. # Side effects: 
  1779. #
  1780.  
  1781. sub DiffCmd {
  1782.     local(@modules) = @_;
  1783.     local($pwd, $i);
  1784.     local($status) = 0;
  1785.     local($current) = 0;
  1786.     local(@options) = (
  1787.     "R", $OPT_TRUE, *current, "Diff with current version",
  1788.     "l", $OPT_FALSE, *recurse, "Recurse on subdirectories",
  1789.     "b", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1790.     "i", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1791.     "w", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1792.     "t", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1793.     "c", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1794.     "e", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1795.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1796.     "h", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1797.     "n", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1798.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1799.     );
  1800.  
  1801.     $recurse = 1;
  1802.     undef($cvsargs);
  1803.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  1804.     print "@modules\n" if ($debug);
  1805.     if ($#modules < $[) {
  1806.     push(@modules, ".");
  1807.     }
  1808.     if (! -d $modules[0]) {
  1809.     $status = &Lock("r","."); 
  1810.     if ($status) {
  1811.         return $status;
  1812.     }
  1813.     foreach $i (@modules) {
  1814.         &DiffFile(".", $i, $cvsargs, $current);
  1815.     }
  1816.     } else {
  1817.     $status = &Lock("r",@modules);
  1818.     if ($status) {
  1819.         return $status;
  1820.     }
  1821.     $pwd = $ENV{'PWD'};
  1822.  
  1823.     foreach $i (@modules) {
  1824.         printf("DiffCmd $i\n") if ($debug);
  1825.         &Chdir($i) == 0 || return 1; 
  1826.         $status = &Diff($i, $cvsargs, $current);
  1827.         if ($status) {
  1828.         return $status;
  1829.         }
  1830.         &Chdir($pwd) == 0 || return 1; 
  1831.     }
  1832.     }
  1833.     return $status;
  1834. }
  1835.  
  1836. #
  1837. # Cvs($path, $command)
  1838. #
  1839. # Run a cvs command in the current directory and its subdirectories.
  1840. # Any output from the command is printed.  The command is not executed
  1841. # in any "SCVS" subdirectories.
  1842. #
  1843. # Results: 0 if successful, 1 otherwise
  1844. #
  1845. # Side effects: 
  1846. #
  1847. sub Cvs {
  1848.     local($path) = shift;
  1849.     local($command) = shift;
  1850.     local($pwd) = $ENV{'PWD'};
  1851.     local($status) = 0;
  1852.     local($output, $tail);
  1853.  
  1854.     if (!-d "CVS.adm") {
  1855.     return 0;
  1856.     }
  1857.     $tail = substr($path, rindex($path, '/') + 1);
  1858.     if ($tail eq "SCVS") {
  1859.     return 0;
  1860.     }
  1861.     printf("%s\n", $path);
  1862.     system("cvs -d $cvsroot $cvsCmdArgs $command");
  1863.     if ($recurse) {
  1864.     $status = &AllSubdirs($path, "Cvs", $command);
  1865.     }
  1866.     return $status;
  1867. }
  1868.  
  1869.  
  1870. #
  1871. # CvsCmd($command, @modules)
  1872. #
  1873. # Runs a cvs command on each module and its subdirectories.
  1874. # Any output from the command is printed.
  1875. #
  1876. # Results: 0 if successful, 1 otherwise
  1877. #
  1878. # Side effects: 
  1879. #
  1880.  
  1881. sub CvsCmd {
  1882.     local($command) = shift;
  1883.     local(@modules) = @_;
  1884.     local($i, @args);
  1885.     local($status) = 0;
  1886.     local($path);
  1887.     local($pwd) = $ENV{'PWD'};
  1888.     local(@options) = (
  1889.     "l", $OPT_FALSE, *recurse, "Don't recurse on subdirs",
  1890.     "L", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1891.     "R", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1892.     "h", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1893.     "t", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1894.     "b", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1895.     "d", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1896.     "l", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1897.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1898.     "s", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1899.     "w", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1900.     );
  1901.  
  1902.  
  1903.     $recurse = 1;
  1904.     undef($cvsargs);
  1905.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  1906.  
  1907.     if ($#modules < $[) {
  1908.     push(@modules, ".");
  1909.     }
  1910.     if (! -d $modules[0]) {
  1911.     $status = &Lock("r","."); 
  1912.     if ($status) {
  1913.         return $status;
  1914.     }
  1915.     $tmp = "cvs -d $cvsroot $cvsCmdArgs $command $cvsargs @modules";
  1916.     print "$tmp\n" if ($debug);
  1917.     system($tmp);
  1918.     } else {
  1919.     $status = &Lock("r", @modules); 
  1920.     if ($status) {
  1921.         return $status;
  1922.     }
  1923. module: 
  1924.     foreach $i (@modules) {
  1925.         &Chdir($i) == 0 || return 1; 
  1926.         $status = &Cvs($i, $command);
  1927.         &Chdir($pwd) == 0 || return 1; 
  1928.     }
  1929.     }
  1930.     return $status;
  1931. }
  1932.  
  1933.  
  1934.  
  1935. #
  1936. # Exit
  1937. #
  1938. # Exit with a status of 1.
  1939. #
  1940. # Results: Doesn't return
  1941. #
  1942. # Side effects: The script exits.
  1943. #
  1944.  
  1945.  
  1946. sub Exit {
  1947.     exit(1);
  1948. }
  1949.  
  1950.  
  1951. #
  1952. # Usage(@optionArray)
  1953. #
  1954. # Print out help information.
  1955. #
  1956. # Results: None
  1957. #
  1958. # Side effects: Stuff is printed
  1959. #
  1960. sub Usage {
  1961.     local(@options) = @_;
  1962.     local(%info) = (("unpack", "Create symbolic links"),
  1963.             ("checkout", "Checkout a copy of a module"),
  1964.             ("unlock", "Unlock a module"),
  1965.             ("lock", "Lock a module"),
  1966.             ("update", "Update a copy of a module"),
  1967.             ("done", "User is done with a module"),
  1968.             ("commit", "Commit changes to a module"),
  1969.             ("who", "Print a list of users with copies of a module"),
  1970.             ("diff", "Do rcsdiff on files you have changed"),
  1971.             ("status", "Print out rcs status of files"),
  1972.             ("log", "Print rcs log of files"),
  1973.             ("join", "Merge in new vendor release"),
  1974.             ("patch", "Create a patch file"),
  1975.             ("tag", "Tag a version"));
  1976.  
  1977.     &Opt_PrintUsage(@options);
  1978.     printf("\nValid commands are:\n");
  1979.     foreach $i sort ("unpack", "checkout", "unlock", "lock", "update", 
  1980.             "done", "commit", "who", "diff", "status", "log",
  1981.             @cvsCmds) {
  1982.     printf("\t$i\t%s\n", $info{$i});
  1983.     }
  1984. }
  1985.  
  1986. #
  1987. # Error($status, @args)
  1988. #
  1989. # Prints @args to STDERR, and returns $status
  1990. #
  1991. # Results: $status
  1992. #
  1993. # Side effects: Stuff is printed
  1994. #
  1995. sub Error {
  1996.     local($status) = shift;
  1997.     if ($#_ >= $[) {
  1998.     printf(STDERR @_);
  1999.     }
  2000.     return $status;
  2001. }
  2002.  
  2003. #
  2004. # ReadFile($file, $ignoreComments)
  2005. #
  2006. # Reads the contents of the given file.  If $ignoreComments is non-zero
  2007. # then any line beginning with '#' is ignored.  
  2008. #
  2009. # Results: An array containing each line of the file.  If a scalar is
  2010. #     wanted then only the first line is returned.
  2011. #
  2012. # Side effects: 
  2013. #
  2014. sub ReadFile {
  2015.     local($file) = shift;
  2016.     local($ignoreComments) = shift; 
  2017.     local(@contents);
  2018.     open(READ, "$file") ||
  2019.     return &Error(undef, "Open of $file failed: $!\n");
  2020.     if ($ignoreComments) {
  2021.     @contents = grep(!/^#/, <READ>);
  2022.     } else {
  2023.     @contents = <READ>;
  2024.     }
  2025.     close(READ);
  2026.     if ($#contents < $[) {
  2027.     return undef;
  2028.     }
  2029.     if (wantarray) {
  2030.     return @contents;
  2031.     } 
  2032.     return($contents[0]);
  2033. }
  2034.  
  2035. #
  2036. # WriteFile($file, @args)
  2037. #
  2038. # Writes @args to $file.  The file is created if it doesn't exist.
  2039. #
  2040. # Results: 0 if successful, 1 otherwise
  2041. #
  2042. # Side effects:  $file may be created, and it is written.
  2043. #
  2044. sub WriteFile {
  2045.     local($file) = shift;
  2046.     open(WRITE, ">$file") ||
  2047.     return &Error(1, "Open of $file failed: $!\n");
  2048.     print WRITE @_;
  2049.     close(WRITE);
  2050.     return 0;
  2051. }
  2052.  
  2053.  
  2054. #
  2055. # GetModuleName
  2056. #
  2057. # Gets the module name from the name in CVS.adm/Repository and %dirMap.
  2058. #
  2059. # Results: The module name.
  2060. #
  2061. # Side effects:  
  2062. #
  2063. sub GetModuleName {
  2064.     local($dir);
  2065.     local($index);
  2066.     if (!defined(%dirMap)) {
  2067.     &ModMap;
  2068.     }
  2069.     $dir = &ReadFile("CVS.adm/Repository");
  2070.     chop($dir);
  2071.     printf("$dir\n") if ($debug);
  2072.     if (!defined($dir)) {
  2073.     return undef;
  2074.     }
  2075.     while($dir ne "") {
  2076.     if (defined($dirMap{$dir})) {
  2077.         printf("Module $dirMap{$dir}\n") if ($debug);
  2078.         return $dirMap{$dir};
  2079.     }
  2080.     $index = rindex($dir, '/');
  2081.     if ($index < $[) {
  2082.         last;
  2083.         return $dir;
  2084.     }
  2085.     $dir = substr($dir, 0, $index);
  2086.     }
  2087.     return $dir;
  2088. }
  2089.  
  2090.  
  2091. #
  2092. # Chdir($dir)
  2093. #
  2094. # Changes the current working directory to $dir.  If the command fails
  2095. # an error message is printed. 
  2096. #
  2097. # Results: 0 if successful, 1 otherwise
  2098. #
  2099. # Side effects:  The current working directory is changed, and $ENV{'PWD'}
  2100. #     set to the new working directory.
  2101. #
  2102. sub Chdir {
  2103.     &chdir($_[0]) ||
  2104.     return &Error(1, "Chdir to %s from %s failed: $!\n", 
  2105.         $_[0], $ENV{'PWD'});
  2106.     return 0;
  2107. }
  2108.  
  2109. #
  2110. # ModMap
  2111. #
  2112. # Creates a mapping of module name to its subdirectory in the repository,
  2113. # and a mapping from the subdirectory to the module name.
  2114. #
  2115. # Results: 0 if successful, 1 otherwise
  2116. #
  2117. # Side effects:  The %modMap and %dirMap are filled in.
  2118. #
  2119.  
  2120. sub ModMap {
  2121.     local($module, $dir);
  2122.     open(MOD, "cvs -d $cvsroot co -c |") ||
  2123.     return &Error(1, "Can't do \"cvs co -c\"\n");
  2124.     undef %modMap;
  2125.     while(<MOD>) {
  2126.     if (/^(\S+)\s+(\S+)/) {
  2127.         $modMap{$1} = $2;
  2128.         $dirMap{$2} = $1;
  2129.     }
  2130.     }
  2131.     close(MOD);
  2132. }
  2133.  
  2134. #
  2135. # Main
  2136. #
  2137. #
  2138. $SIG{'INT'} = Exit;
  2139. &initpwd;
  2140. $tmpfile = "#SCVS.$$";
  2141. $status = 0;
  2142. if (&Config) {
  2143.     exit(1);
  2144. }
  2145. $command = shift;
  2146. if (!defined($command)) {
  2147.     &Usage(@options);
  2148.     exit(1);
  2149. }
  2150. printf("$command: %s\n", join(' ', @ARGV)) if ($debug);
  2151.  
  2152. if (($command eq "pack") || ($command eq "unpack")) {
  2153.     local(@options) = ("l", $OPT_FALSE, *recurse, "Recurse on subdirectories");
  2154.     &Opt_Parse(*ARGV, @options, 0);
  2155.     $status = &PackCmd($command, @ARGV);
  2156. } elsif (($command eq "checkout") || ($command eq "co")) {
  2157.     $command = "checkout";
  2158.     $status = &Checkout(@ARGV);
  2159. } elsif ($command eq "unlock") {
  2160.     $status = &UnlockCmd(@ARGV);
  2161. } elsif ($command eq "lock") {
  2162.     $status = &LockCmd(@ARGV);
  2163.     undef(@locks);
  2164. } elsif ($command eq "update") {
  2165.     $status = &UpdateCmd(1, @ARGV);
  2166. } elsif ($command eq "done") {
  2167.     $status = &DoneCmd(@ARGV);
  2168. } elsif (($command eq "commit") || ($command eq "ci")) {
  2169.     $status = &CommitCmd(@ARGV);
  2170. } elsif ($command eq "who") {
  2171.     $status = &WhoCmd(@ARGV);
  2172. } elsif ($command eq "add") {
  2173.     $status = &AddCmd(@ARGV);
  2174. } elsif ($command eq "remove") {
  2175.     $status = &RemoveCmd(@ARGV);
  2176. } elsif ($command eq "info") {
  2177.     $status = &InfoCmd(@ARGV);
  2178. } elsif ($command eq "diff") {
  2179.     $status = &DiffCmd(@ARGV);
  2180. } elsif (($command eq "status") || ($command eq "log")) {
  2181.     $status = &CvsCmd($command, @ARGV);
  2182. } elsif (grep($command eq $_, @cvsCmds)) {
  2183.     system("cvs -d $cvsroot $cvsCmdArgs $command @ARGV");
  2184.     $status = 0;
  2185. } else {
  2186.     printf("Bad command: $command\n");
  2187.     &Usage(@options);
  2188.     exit(1);
  2189. }
  2190.  
  2191. # Unlock any modules we may have locked.
  2192.  
  2193. if ($#locks >= $[) {
  2194.     &Unlock(0, @locks);
  2195. }
  2196. if ($status) {
  2197.     printf("$command failed\n");
  2198. }
  2199. exit($status);
  2200.